/*
 * Copyright 2006 - 2007 The MITRE Corporation. All rights reserved.
 * Written by Ayal Spitz
 *
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1
 * (the "License"); you may not use this file except in compliance
 * with the License and the following two conditions:
 * 
 * The US Government will not be charged any license fee and/or royalties 
 * related to this software.
 * 
 * Neither name of The MITRE Corporation; nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * You may obtain a copy of the License at
 *
 *     http://www.gnu.org/copyleft/lesser.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package org.mitre.streamingRSS;

import java.io.CharArrayReader;
import java.io.IOException;
import java.util.List;

import net.iharder.Base64;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.SyndFeedOutput;

/**
 * The SubListener class is an implementation of 
 * spitz.ayal.irss.xmpp.SubListener interface. It's desgined to be the base for
 * any subscription listener interested in getting instantRSS messages.
 * 
 * @author Ayal Spitz
 *
 */
public abstract class SubListener extends org.mitre.streamingRSS.xmpp.SubListener {
// ### Class Fields ###########################################################
	private SAXBuilder			saxBuilder = null;
	private SyndFeedOutput 		feedOutput = null;
	private SyndFeedInput		feedInput = null;

	private SyndFeed			feed = null;
	
// ### Class Constructor ######################################################
	public SubListener(){
		saxBuilder = new SAXBuilder();
		feedOutput = new SyndFeedOutput();
        feedInput = new SyndFeedInput();
	}
	
// ### Class Methods ##########################################################
	/**
	 * This method implements the newMsgs method. It converts the given
	 * messages into a feed. If the feed is null then the method builds the 
	 * basic SyndFeed from the MultiUserChat subject and then add entries to
	 * it.  If the feed is not null then the method converts the strings to
	 * entries and adds them to the feed
	 * 
	 * @param msgList		A list of messages received
	 */
	@SuppressWarnings("unchecked")
	public void newMsgs(List<String> msgList){
		if (feed == null){
			feed = buildFeedFromSubject();
			feed = addEntries(feed,msgList);
		} else {
			List<SyndEntry>		entries = null;
			
			entries = toEntries(msgList);
			feed.getEntries().addAll(entries);
		}
		newFeed(feed);
	}
	
	/**
	 * This method queries the MultiUserChat room subject to build a new SyndFeed
	 * 
	 * @return					A SyndFeed constructed from the subject line of 
	 * 							the chat room. This should have source feed info
	 */
	private SyndFeed buildFeedFromSubject(){
		String				feedXML = null;
		SyndFeed			syndFeed = null;
		CharArrayReader		reader = null;
		
		try{
			feedXML = muc.getSubject();
			// Decompress and decode from Base64/GZIP to Fast Infoset
			feedXML = new String(Base64.decode(feedXML));
			reader = new CharArrayReader(feedXML.toCharArray());
			
			syndFeed = feedInput.build(reader);
		}catch(Exception e){ e.printStackTrace(); }
		
		return syndFeed;
	}
	
	/**
	 * This method builds a SyndFeed with the bearest of information
	 * 
	 * @return					A SyndFeed with the bearest of information
	 */
	private SyndFeed buildEmptyFeed(){
		SyndFeed			syndFeed = null;

		// Build the feed
		syndFeed = new SyndFeedImpl();
		syndFeed.setFeedType("rss_2.0");
		syndFeed.setTitle("temp");
		syndFeed.setDescription("temp");
		syndFeed.setLink("http://localhost/temp");
		
		return syndFeed;
	}

	/**
	 * This method converts the feed into a DOM model it then converts the list
	 * of msgs in msgList into XML and then attaches the XML msgs to the DOM
	 * model. Finally the DOM model is converted back to a SyndFeed which is 
	 * returned to the user.
	 * 
	 * @param syndFeed			The SyndFeed to attach the new elements to
	 * @param msgList			A list of strings that contain item elements to
	 * 							attach to the SyndFeed
	 * 
	 * @return					The SyndFeed passed in with the attached elements
	 */
	private SyndFeed addEntries(SyndFeed syndFeed, List<String> msgList){
		Document			feedJDOM = null;
		Element				channel = null;
		Element				element = null;
		String				str = null;
		
		try{
			// Build the JDOM for this feed
			feedJDOM = feedOutput.outputJDom(syndFeed);
			// Find the channel node of this feed
			channel = feedJDOM.getRootElement().getChild("channel");
			
			// Add the messages to the channel node
			for(String msg:msgList){
				str = new String(Base64.decode(msg));
				element = toElement(str);
				channel.addContent( element );
			}
	        
			// Convert the feed back from a JDOM into a SyndFeed
			syndFeed = feedInput.build(feedJDOM);
			//feedOutput.output(feed, new PrintWriter(System.out));
			// Pass along the new feed
		}catch(Exception e){ e.printStackTrace(); }
		
		return syndFeed;
	}
	
	/**
	 * This method converts a list of strings to a list of SyndEntrys by utilizing
	 * the addEntries method on an empty feed and then returning the list of
	 * SyndEntrys
	 * 
	 * @param msgList			A list of strings that contain item elements to
	 * 							attach to the SyndFeed
	 * 
	 * @return					A list of SyndEntrys converted from the passed
	 * 							in strings
	 */
	private List<SyndEntry> toEntries(List<String> msgList){
		SyndFeed			syndFeed = null;
		List<SyndEntry>		entries = null;
		
		syndFeed = buildEmptyFeed();
		syndFeed = addEntries(syndFeed,msgList);
		
		entries = ((List<SyndEntry>)(syndFeed.getEntries()));

		return entries;
	}
	
	/**
	 * This method converts a string to an Elmenet. The string is the result of
	 * an XML structure faltened into a string. This method reverses the
	 * process.
	 * 
	 * @param elementStr		The string to be converted to an element
	 * 
	 * @return					An element based on the passed in string
	 * 
	 * @throws IOException
	 * @throws JDOMException
	 */
	protected Element toElement(String elementStr) throws IOException,JDOMException{
		CharArrayReader				reader = null;
		Document					doc = null;
		Element						element = null;
		
		// Convert the source string into a character array reader
		reader = new CharArrayReader(elementStr.toCharArray());
		
		// Convert the char array into an XML document
		doc = saxBuilder.build(reader);
		// Clone the root element of the XML document
		element = ((Element)(doc.getRootElement().clone()));

		// Make sure we close up the reader just so we don't leave loose ends
		reader.close();

		// Return the cloned element to the caller
		return element;
	}
	
	/**
	 * This abstract method is called when the incoming messages have been 
	 * converted into a feed
	 * 
	 * @param syndFeed			The new feed created from the passed in messages
	 */
	public abstract void newFeed(SyndFeed syndFeed);
}
